<?php

namespace Core\Route;

use Core\Route\DataGenerator;
use Core\Route\RouteParser;

class RouteCollector
{
    /** @var RouteParser */
    protected $routeParser;

    /** @var MiddleWare */
    protected $middleWare;

    /** @var DataGenerator */
    protected $dataGenerator;

    /** @var string */
    protected $currentGroupPrefix;

    /**
     * Constructs a route collector.
     *
     * @param RouteParser   $routeParser
     * @param DataGenerator $dataGenerator
     */
    public function __construct(RouteParser $routeParser, DataGenerator $dataGenerator, Middleware $middleWare)
    {
        $this->middleWare = $middleWare;
        $this->routeParser = $routeParser;
        $this->dataGenerator = $dataGenerator;
        $this->currentGroupPrefix = '';
    }

    /**
     * Adds a route to the collection.
     *
     * The syntax used in the $route string depends on the used route parser.
     *
     * @param string|string[] $httpMethod
     * @param string $route
     * @param mixed  $handler
     */
    public function addRoute($httpMethod, $route, $handler)
    {
        $route = $this->currentGroupPrefix . $route;
        $routeDatas = $this->routeParser->parse($route);
        foreach ((array) $httpMethod as $method) {
            foreach ($routeDatas as $routeData) {
                $this->middlewareGenerator(function () use ($method, $routeData, $handler) {
                    $middleData = $this->middleWare->getData();
                    $this->dataGenerator->addRoute($method, $routeData, $handler, $middleData);
                });
            }
        }
    }

    /**
     * 设置当前
     */
    public function middleware(array $mw = [], callable $callback = null)
    {
        $this->middleWare->setCurrent($mw);

        if (!is_null($callback)) {
            $this->middlewareGenerator($callback);
        }

        return $this;
    }

    /**
     * 处理中间
     */
    public function middlewareGenerator(callable $callback = null)
    {
        // 处理中间件
        $previousData = $this->middleWare->getData();
        $this->middleWare->mergeCurrent();

        $callback($this);

        // 处理完子路由后恢复当前中间件数据
        $this->middleWare->setData($previousData);
    }

    /**
     * Create a route group with a common prefix.
     *
     * All routes created in the passed callback will have the given group prefix prepended.
     *
     * @param string $prefix
     * @param callable $callback
     */
    public function group($prefix, callable $callback)
    {
        $this->middlewareGenerator(function () use ($prefix, $callback) {
            $previousGroupPrefix = $this->currentGroupPrefix;
            $this->currentGroupPrefix = $previousGroupPrefix . $prefix;

            // 处理中间件
            $this->middlewareGenerator($callback);

            $this->currentGroupPrefix = $previousGroupPrefix;
        });
    }

    /**
     * Adds a GET route to the collection
     *
     * This is simply an alias of $this->addRoute('GET', $route, $handler)
     *
     * @param string $route
     * @param mixed  $handler
     */
    public function get($route, $handler)
    {
        $this->addRoute('GET', $route, $handler);
    }

    /**
     * Adds a POST route to the collection
     *
     * This is simply an alias of $this->addRoute('POST', $route, $handler)
     *
     * @param string $route
     * @param mixed  $handler
     */
    public function post($route, $handler)
    {
        $this->addRoute('POST', $route, $handler);
    }

    /**
     * Adds a PUT route to the collection
     *
     * This is simply an alias of $this->addRoute('PUT', $route, $handler)
     *
     * @param string $route
     * @param mixed  $handler
     */
    public function put($route, $handler)
    {
        $this->addRoute('PUT', $route, $handler);
    }

    /**
     * Adds a DELETE route to the collection
     *
     * This is simply an alias of $this->addRoute('DELETE', $route, $handler)
     *
     * @param string $route
     * @param mixed  $handler
     */
    public function delete($route, $handler)
    {
        $this->addRoute('DELETE', $route, $handler);
    }

    /**
     * Adds a PATCH route to the collection
     *
     * This is simply an alias of $this->addRoute('PATCH', $route, $handler)
     *
     * @param string $route
     * @param mixed  $handler
     */
    public function patch($route, $handler)
    {
        $this->addRoute('PATCH', $route, $handler);
    }

    /**
     * Adds a HEAD route to the collection
     *
     * This is simply an alias of $this->addRoute('HEAD', $route, $handler)
     *
     * @param string $route
     * @param mixed  $handler
     */
    public function head($route, $handler)
    {
        $this->addRoute('HEAD', $route, $handler);
    }

    /**
     * Returns the collected route data, as provided by the data generator.
     *
     * @return array
     */
    public function getData()
    {
        return $this->dataGenerator->getData();
    }
}
